#include "irq.h"
#include "init.h"
#include "common.h"
#include <linux/slab.h>

struct irq_cfg {
	struct init_block init;
	unsigned int irq;
	bool thread;
	void *dev;
};

static void irq_cleanup(struct init_block *init);
static void irq_drop(struct init_block *init);

int setup_irq(
	unsigned int irq,
	irq_handler_t handler,
	unsigned long flags,
	const char *name,
	void *dev
) 
{
	int ret = 0;
	struct irq_cfg *cfg = kmalloc(sizeof(struct irq_cfg), GFP_KERNEL);
	cfg->init.init_fn = NULL;
	cfg->init.cleanup_fn = irq_cleanup;
	cfg->init.drop_fn = irq_drop;
	cfg->irq = irq;
	cfg->thread = false;
	cfg->dev = dev;
	strlcpy(cfg->init.name, name, BLOCK_NAME_LEN);

	ret = request_irq(
			irq,
			handler,
			flags,
			cfg->init.name,
			dev
	);

	if (ret < 0) {
		kfree(cfg);
		PR_ERR("request irq (%u, %s) failed, ret %d", irq, cfg->init.name, ret);
		return ret;
	}

	return try_init(&cfg->init);
}

int setup_threaded_irq(
	unsigned int irq,
	irq_handler_t handler,
	unsigned long flags,
	const char *name,
	void *dev
) 
{
	int ret = 0;
	struct irq_cfg *cfg = kmalloc(sizeof(struct irq_cfg), GFP_KERNEL);
	cfg->init.init_fn = NULL;
	cfg->init.cleanup_fn = irq_cleanup;
	cfg->init.drop_fn = irq_drop;
	cfg->irq = irq;
	cfg->thread = true;
	cfg->dev = dev;
	strlcpy(cfg->init.name, name, BLOCK_NAME_LEN);

	PR_INFO("request threaded irq");
	ret = request_threaded_irq(
			irq,
			NULL,
			handler,
			flags | IRQF_ONESHOT,
			cfg->init.name,
			dev
	);

	if (ret < 0) {
		kfree(cfg);
		PR_ERR("request irq (%u, %s) failed, ret %d", irq, cfg->init.name, ret);
		return ret;
	}

	return try_init(&cfg->init);
}

void irq_cleanup(struct init_block *init)
{
	struct irq_cfg *cfg = container_of(init, struct irq_cfg, init);
	PR_INFO("free irq (%u, %s)", cfg->irq, cfg->init.name);
	free_irq(cfg->irq, cfg->dev);
}

void irq_drop(struct init_block *init) 
{
	struct irq_cfg *cfg = container_of(init, struct irq_cfg, init);
	kfree(cfg);
}
